#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>

#include <ipms.h>
#include <pcd_api.h>
#include "ipms_console.h"
#include "ipms_dicop.h"


pthread_t resp_recv_thread;
static pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER;

static int run_mode = MODE_CONSOLE;
static int goodbye = 0;


void sighandler_graceful_exit(int sig)
{
    printf("Gracefully exiting thread #%d\n", (int)pthread_self());
    pthread_exit(0);
    goodbye = 1;
}

void *thread_response_recv(void *argp)
{
    struct pcd_struct *pcd_list_node, *__packet;
    time_t timestamp;
    struct tm *local_tm;

    signal(SIGINT, sighandler_graceful_exit);
    signal(SIGQUIT, sighandler_graceful_exit);
    signal(SIGTERM, sighandler_graceful_exit);

    while (1) {
        time(&timestamp);
        local_tm = localtime(&timestamp);

        __packet = pcd_read_packet();

        if (__packet) {
            switch (__packet->command) {
                case RCMD_JOIN:
                    pcd_list_node = __packet;
                    pcd_list_insert(pcd_list_node);

                    snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                             local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                             local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                    pthread_mutex_lock(&output_mutex);

                    if (run_mode == MODE_CONSOLE) {
                        ConsoleJoinCallback(pcd_list_node);
                    }
                    else if (run_mode == MODE_DAEMON) {
                        DicopJoinCallback(pcd_list_node);
                    }

                    pthread_mutex_unlock(&output_mutex);

                    break;
                case RCMD_REPORT:
                    if ((pcd_list_node = pcd_list_search_by_id(__packet->id)) != NULL) {
                        pcd_list_node->reports = __packet->reports;

                        //PRINT_DEBUG("[REQ DBG]ID=%d Volt=%d Curr=%d InstantPower=%d AccumPower=%d\n",
                        //            __packet->reports.voltage, __packet->reports.current,
                        //            __packet->reports.instant_power, __packet->reports.accum_power);

                        snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                                 local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                                 local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                        pthread_mutex_lock(&output_mutex);

                        if (run_mode == MODE_CONSOLE) {
                            ConsoleReportCallback(pcd_list_node);
                        }
                        else if (run_mode == MODE_DAEMON) {
                            DicopReportCallback(pcd_list_node);
                        }

                        pthread_mutex_unlock(&output_mutex);
                    }

                    free(__packet);

                    break;
                case RCMD_MAC:
                    if ((pcd_list_node = pcd_list_search_by_id(__packet->id)) != NULL) {
                        strncpy(pcd_list_node->mac_addr, __packet->mac_addr, 24);

                        snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                                 local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                                 local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                        pthread_mutex_lock(&output_mutex);

                        if (run_mode == MODE_CONSOLE) {
                            ConsoleMACCallback(pcd_list_node);
                        }
                        else if (run_mode == MODE_DAEMON) {
                            DicopMACCallback(pcd_list_node);
                        }

                        pthread_mutex_unlock(&output_mutex);
                    }

                    free(__packet);

                    break;
                case RCMD_STATUS:
                    if ((pcd_list_node = pcd_list_search_by_id(__packet->id)) != NULL) {
                        pcd_list_node->time_left = __packet->time_left;
                        pcd_list_node->status = __packet->status;

                        snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                                 local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                                 local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                        pthread_mutex_lock(&output_mutex);

                        if (run_mode == MODE_CONSOLE) {
                            ConsoleStatusCallback(pcd_list_node);
                        }
                        else if (run_mode == MODE_DAEMON) {
                            DicopStatusCallback(pcd_list_node);
                        }

                        pthread_mutex_unlock(&output_mutex);
                    }

                    free(__packet);

                    break;
                case RCMD_EVENT:
                    if ((pcd_list_node = pcd_list_search_by_id(__packet->id)) != NULL) {
                        pcd_list_node->event = __packet->event;

                        snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                                 local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                                 local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                        pthread_mutex_lock(&output_mutex);

                        if (run_mode == MODE_CONSOLE) {
                            ConsoleEventProcess(pcd_list_node, RCMD_EVENT);
                        }
                        else if (run_mode == MODE_DAEMON) {
                            DicopEventProcess(pcd_list_node, RCMD_EVENT);
                        }

                        pthread_mutex_unlock(&output_mutex);
                    }

                    free(__packet);

                    break;
                case RCMD_AUTO_REPORT:
                    if ((pcd_list_node = pcd_list_search_by_id(__packet->id)) != NULL) {
                        pcd_list_node->reports = __packet->reports;

                        snprintf(pcd_list_node->last_update, 20, "%04d/%02d/%02d %02d:%02d:%02d",
                                 local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday,
                                 local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);

                        pthread_mutex_lock(&output_mutex);

                        if (run_mode == MODE_CONSOLE) {
                            ConsoleEventProcess(pcd_list_node, RCMD_AUTO_REPORT);
                        }
                        else if (run_mode == MODE_DAEMON) {
                            DicopEventProcess(pcd_list_node, RCMD_AUTO_REPORT);
                        }

                        pthread_mutex_unlock(&output_mutex);
                    }

                    free(__packet);

                    break;
                /* case RCMD_ALARM: */
            }
        }
    }
}

int main(int argc, char *argv[])
{
    int c;
    char cmd[64];
    char port[16] = { '\0', };
    char mode[16] = { '\0', };

    while ((c = getopt(argc, argv, "p:m:")) != -1) {
        switch (c) {
            case 'p':
                strncpy(port, optarg, strlen(optarg) > 15? 15: strlen(optarg));
                break;
            case 'm':
                strncpy(mode, optarg, strlen(optarg) > 15? 15: strlen(optarg));
                break;
        }
    }

    if (strlen(port) > 0)
        pcd_open(port);
    else
        pcd_open(PCD_DEV);

    pthread_create(&resp_recv_thread, NULL, &thread_response_recv, NULL);

    if (strncmp(mode, "daemon", 6) == 0) {
        run_mode = MODE_DAEMON;
        DicopIPMSInit();
        pthread_join(resp_recv_thread, NULL);
        DicopIPMSExit();
    }
    else {
        run_mode = MODE_CONSOLE;
        while (!goodbye) {
            pthread_mutex_lock(&output_mutex);
            printf("CMD> ");
            pthread_mutex_unlock(&output_mutex);
            if(fgets(cmd, CMD_MAX_SZ, stdin) == NULL)
                continue;

            ConsoleExecCmd(cmd);
        }
    }

    pcd_close();

    return 0;
}
